from PySide6.QtCore import Qt
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QApplication, QVBoxLayout, QPushButton, QWidget, QGridLayout, QListWidget, \
    QAbstractItemView, QListWidgetItem
import sys
from functools import reduce


class CustomWidget(QWidget):
    def __init__(self) -> None:
        super().__init__()
        self.setWindowTitle('12. QListWidget')

        self.list_widget = QListWidget(self)
        # DOC: 13.QListWidget基础属性设置
        # 设置为复选框模式
        self.list_widget.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
        # 设置子菜单的可拖到调整顺序
        self.list_widget.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)

        # DOC: 11.给ListWidget设置右键菜单（上下文菜单）
        self.initListWidgetContextMenu()

        # DOC: 1.【添加】
        self.list_widget.addItem("One")
        self.list_widget.addItems(["Two", "Three"])
        self.list_widget.addItem(QListWidgetItem('Four'))
        # 添加的时候设置开启复选框
        self.listWidgetAddCheckableItem('Five', is_checked=False)
        self.listWidgetAddCheckableItem('Six', is_checked=False)

        # 遍历 QListWidget
        for i in range(self.list_widget.count()):
            print(self.list_widget.item(i).text())

        # DOC: 2.【查找元素】
        # 2.1 查找指定下标的元素
        index_item: QListWidgetItem = self.list_widget.item(2)
        # 2.2 获取选中行
        current_index: int = self.list_widget.currentRow()
        # 2.3 获取当前激活的元素 QListWidgetItem
        current_item: QListWidgetItem = self.list_widget.currentItem()
        # 2.4 获取选中的所有行
        item_list: list[QListWidgetItem] = self.list_widget.selectedItems()
        # 2.5 获取指定item元素在所有元素中的位置
        item_index: int = self.list_widget.row(index_item)

        # 2.6 查找匹配元素
        item_list: list[QListWidgetItem] = self.list_widget.findItems('o', Qt.MatchFlag.MatchContains)
        """
            Qt.MatchFlag 常用的匹配规则：
                1. MatchExactly >>> 精确匹配
                2. MatchContains、 MatchStartsWith/MatchEndsWith、  >>> 包含
                3. MatchCaseSensitive >>> 大小写敏感【由此推测出默认大小写不敏感】
                4. MatchRegularExpression >>> 正则匹配
        """


        # DOC: 3.【修改】
        # 设置显示的文字
        index_item.setText('xxxx')
        # 设置提示
        index_item.setToolTip('这是提示')

        # DOC: 5.事件信号绑定
        # 5.1 修改选中信号
        self.list_widget.currentItemChanged.connect(self.list_widget_current_item_changed)
        self.list_widget.currentTextChanged.connect(self.list_widget_current_text_changed)
        # 5.2 双击信号
        self.list_widget.itemDoubleClicked.connect(lambda: print(f'双击 {self.list_widget.currentItem().text()}'))
        # 5.2 元素发生改变信号(通常用用来处理选中状态发生改变)
        self.list_widget.itemChanged.connect(lambda item: print(f'修改 第{self.list_widget.row(item) + 1}行 元素值为 {item.text()}'))
        index_item.setText('xxxx2')

        # 2. 给 QListWidget 内的item添加维护功能按键
        btn_add_item = QPushButton("Add Item")
        btn_add_item.clicked.connect(self.add_item)

        btn_insert_item = QPushButton("Insert Item After Selected")
        btn_insert_item.clicked.connect(self.insert_item)

        btn_del_item = QPushButton("Del Item")
        btn_del_item.clicked.connect(self.del_item)

        btn_item_count = QPushButton("Count Item")
        btn_item_count.clicked.connect(self.count_item)

        btn_selected_item = QPushButton("Selected Item")
        btn_selected_item.clicked.connect(self.selected_item)

        btn_checked_item = QPushButton("Checked Item")
        btn_checked_item.clicked.connect(self.checked_item)

        btn_scroll_to_item = QPushButton("滚动到指定的元素：scrollToItem")
        btn_scroll_to_item.clicked.connect(self.scroll_to_item)

        v_layout = QVBoxLayout()
        v_layout.addWidget(self.list_widget)
        v_layout.addWidget(btn_add_item)
        v_layout.addWidget(btn_insert_item)
        v_layout.addWidget(btn_del_item)
        v_layout.addWidget(btn_item_count)
        v_layout.addWidget(btn_selected_item)
        v_layout.addWidget(btn_checked_item)
        v_layout.addWidget(btn_scroll_to_item)

        self.setLayout(v_layout)

    def list_widget_current_item_changed(self, current, perious):
        print(f'item_changed: current = {current}, before = {perious}')

    def list_widget_current_text_changed(self, current):
        print(f'text_changed: current = {current}')

    def add_item(self):
        self.list_widget.addItem(QListWidgetItem("New Item"))

    # DOC: 3.【修改 - 插入元素】
    def insert_item(self):
        ## 实现方式1： 略复杂
        # if self.list_widget.selectedItems():
        #     # 如果有选中的：获取当前选中的最后一条记录的下标
        #     index = self.list_widget.row(self.list_widget.selectedItems()[-1])
        # else:
        #     index = self.list_widget.count()
        # index += 1

        ## 实现方式2： 推荐
        index = self.list_widget.currentRow() + 1

        self.list_widget.insertItem(index, QListWidgetItem("New Item"))
        # 同样也支持批量插入（！但是批量插入只支持字符串Sequence, 不支持QListWidgetItem）
        # self.list_widget.insertItems(index, [f"New Item {i}" for i in range(5)])

    # DOC: 4.删除元素
    def del_item(self):
        # 4.1 获取当前选择的行（如果没有返回0）：self.list_widget.currentRow()
        self.list_widget.takeItem(self.list_widget.currentRow())
        # 4.2 清空所有元素
        # self.list_widget.clear()

    def count_item(self):
        print(f'Item count: {self.list_widget.count()}')

    def selected_item(self):
        item_list = self.list_widget.selectedItems()
        s = reduce(lambda a, b: f'{a} {b.text()} -', item_list, 'Selected Items: ')
        print(s[:-1])
        # for i in item_list:
        #     print(i.text())

    def checked_item(self):
        # 注意，checked不同于selected
        check_items = []
        for i in range(self.list_widget.count()):
            item = self.list_widget.item(i)
            if item.checkState() == Qt.CheckState.Checked:
                check_items.append(item)

        print(f'获取checked结果：{reduce(lambda a, b: f"{a} {b.text()}", check_items, '')}')

    # DOC: 6.滚动条操作
    def scroll_to_item(self):
        # 6.1 滚动到顶部
        # self.list_widget.scrollToTop()
        # 6.2 到底部
        # self.list_widget.scrollToBottom()
        # 6.3 滚动到指定item位置（一般用于查找元素后快速定位到查找元素）
        self.list_widget.scrollToItem(self.list_widget.currentItem())

    def initListWidgetContextMenu(self):
        self.list_widget.setContextMenuPolicy(Qt.ContextMenuPolicy.ActionsContextMenu)
        # 这里一定要加self，否则对象会被回收，导致右键菜单无法正常显示（规范一点写，应该将这些变量加到__init__()中）
        self.modifyValAction = QAction('修改值')
        self.cleanValAction = QAction('删除')
        self.ascAction = QAction('升序排列')
        self.descAction = QAction('降序排列')
        self.toTopAction = QAction('返回顶部')
        self.toBottomAction = QAction('滚动到底部')
        # 这个无法实现，因为右键一点击，这个item就被选中了（实际并没选中），就变成滚动到当前位置了
        # self.toCurrentSelectedAction = QAction('返回当前选中选中')
        self.list_widget.addActions([self.modifyValAction, self.cleanValAction, self.ascAction, self.descAction,
                                     self.toTopAction, self.toBottomAction])

        self.modifyValAction.triggered.connect(lambda: self.list_widget.currentItem().setText('modified val'))
        self.cleanValAction.triggered.connect(self.del_item)
        # DOC: 12.排序
        self.ascAction.triggered.connect(lambda: self.list_widget.sortItems(Qt.SortOrder.AscendingOrder))
        self.descAction.triggered.connect(lambda: self.list_widget.sortItems(Qt.SortOrder.DescendingOrder))

        self.toTopAction.triggered.connect(lambda: self.list_widget.scrollToTop())
        self.toBottomAction.triggered.connect(lambda: self.list_widget.scrollToBottom())
        # self.toCurrentSelectedAction.triggered.connect(lambda: self.list_widget.scrollToItem(self.list_widget.currentItem()) if self.list_widget.currentItem() else print('未找到当前元素'))

    def listWidgetAddCheckableItem(self, text, is_checked):
        item = QListWidgetItem(text)
        item.setFlags(item.flags() | Qt.ItemFlag.ItemIsSelectable)
        # 无论宣布选中，一定要设置状态，否则选择框出不来
        item.setCheckState(Qt.CheckState.Checked if is_checked else Qt.CheckState.Unchecked)
        # 配合 5.2的 itemChanged 信号，!!在不允许修改item其他内容的时候，可判断是否选中
        self.list_widget.addItem(item)


app = QApplication(sys.argv)

window = CustomWidget()
window.show()

app.exec()
